// Authors: Matthew DeNovio
#include "World.h"
#include "Camera.h"
#include <fstream>
#include <string>
#include <iostream>

using namespace std;

// Constructor
World::World()
{
	_pGameCamera = NULL;


	// Sets the pointers to Null
	for(U8 i = 0; i < TILE_COUNT; i++)
		tiles[i] = NULL;
}

// Initializes the World
#pragma region initWorld

void World::init(Camera * gameCamera)
{
	deleteWorld();
	_pGameCamera = gameCamera;

	// Creates the World
	_pWorldMapTile = (Tile ***)(malloc(sizeof(Tile**) * _pGameCamera->locationScreenTileHeight));
	*_pWorldMapTile = (Tile **)(malloc(sizeof(Tile*) * _pGameCamera->locationScreenTileHeight * _pGameCamera->locationScreenTileWidth));
	for(U16 r = 1; r < _pGameCamera->locationScreenTileHeight; r++)
		*(_pWorldMapTile + r) = *_pWorldMapTile + _pGameCamera->locationScreenTileWidth * r;
	createBlankWorld();
}

#pragma endregion

// Deletes the World
#pragma region DeleteWorld

void World::deleteWorld()
{
	if(_pGameCamera != NULL)
	{
		for (U8 i = 0; i < _pGameCamera->locationScreenTileHeight; i++)
			free(_pWorldMapTile[i]);	// delete the arrays in the 2nd dimension
		free(_pWorldMapTile);		// delete the first dimension
	}
}

#pragma endregion

// Deconstructor
World::~World(void)
{
	deleteWorld();

	deleteTiles();
	delete tiles;
}

// blankWorld
void World::createBlankWorld()
{
	for (U16 i = 0; i < _pGameCamera->locationScreenTileHeight; i++) 
	{
		for (U16 j = 0; j < _pGameCamera->locationScreenTileWidth; j++)
		{
			//_pWorldMapTile[i][j] = NULL;
		}
	}
}

// Loads the Map 
#pragma region LoadMap

void World::loadMap(U8 levelNumber)
{
	deleteTiles();

	ostringstream oss;
	oss.clear();
	oss << "Levels/level" << (U16)levelNumber << ".txt";		// Convert to to short so its not a char


	ifstream myfile (oss.str());
	string data;

	// if it's open
	if(myfile.is_open())
	{
		myfile >> data;
		U16 imgx = atoi(data.c_str());

		myfile >> data;
		U16 imgy = atoi(data.c_str());

		myfile >> data;
		_width = atoi(data.c_str());

		myfile >> data;
		_height = atoi(data.c_str());


		// Allocates space for Pointers to the Images 
		_pWorldMapTile = (Tile ***)(malloc(sizeof(Image**) * _height));
		*_pWorldMapTile = (Tile **)(malloc(sizeof(Image*) * _height * _width));
		for(U16 r = 1; r < _height; r++)
			*(_pWorldMapTile + r) = *_pWorldMapTile + _width * r;


		// Loop through Tiles #'s in file and Point to them
		for (U16 i = 0; i < _height; i++) {

			myfile >> data;

			for (U16 j = 0; j < _width; j++)
			{
				if (data[j] > TILE_COUNT - 1  || data[j] < 0)
				{
					data[j] = 0;
				}

				// Create the Tile if not Allocated and Point to it
				createTile(data[j]);
				_pWorldMapTile[i][j] = tiles[data[j]];
			}
		}

		// Set Camera to this location
		_pGameCamera->moveCamera(imgx, imgy);
	}

	myfile.close();
	drawScreen();
}

#pragma endregion

// Draws the Current visible Screen
#pragma region DrawCurrentScreen

void World::drawCurrentScreen()
{
	Point cursorLoc;
	cursorLoc.x = _pGameCamera->getTopLeft().x;
	cursorLoc.y = _pGameCamera->getTopLeft().y;


	// Draw the Tiles in the Current Camera View
	for (U16 k = cursorLoc.y / _pGameCamera->TILE_DIM; k < _pGameCamera->getBottomRight().y / _pGameCamera->TILE_DIM; k++)
	{

		cursorLoc.x = _pGameCamera->getTopLeft().x;

		_pGameCamera->setCursorPosition(cursorLoc.x, cursorLoc.y);

		for (U16 l = cursorLoc.x / _pGameCamera->TILE_DIM; l < _pGameCamera->getBottomRight().x / _pGameCamera->TILE_DIM; l++)
		{
			// Draw Tile
			_pWorldMapTile[k][l]->Image().DrawImage(cursorLoc, _pGameCamera);


			cursorLoc.x += _pGameCamera->TILE_DIM;
			_pGameCamera->setCursorPosition(cursorLoc.x, cursorLoc.y);

		}

		cursorLoc.y += _pGameCamera->TILE_DIM;
	}

	//_pGameCamera->refocus();
}

#pragma endregion

// Draw Screen Around the Screen
void World::drawFullScreen()
{
	for (U16 i = 0; i < _pGameCamera->locationScreenTileHeight - 1; i++)		// ignore last line(blank) or else it removes the first line completely
	{
		for (U16 j = 0; j < _pGameCamera->locationScreenTileWidth; j++)
		{
			//cout << (worldMap[i][j]);
		}
	}
}


// Checks if Tile is allocated and then Allocates it
#pragma region CreateTile

void World::createTile(I8 tileNum)
{

	ostringstream oss;
	oss.clear();

	oss << "Tiles/" << (U16)tileNum;

	// Create Image if not already allocated
	if(tiles[tileNum] == NULL)
		tiles[tileNum] = new Tile(oss.str());	// "Tiles/tileNum/image.txt
}

#pragma endregion



void World::drawScreen() 
{

	drawCurrentScreen();
	drawFullScreen();

	_pGameCamera->refocus();
}

// Delete Allocated Tiles when Switching Locations
void World::deleteTiles()
{
	for(U8 i = 0; i < TILE_COUNT; i++)
	{
		if(tiles[i] != NULL) 
		{
			delete tiles[i];
			tiles[i] = NULL;
		}
	}
}

// Draw what becomes revealed on screen
void World::drawOffsetParts(Point& shiftAmount, Point& playerLoc)
{
	Point cursorLoc;

	U16 occupiedSq;

	U8 influence = 0;

	// Shift Amount X
	if(shiftAmount.x)
	{
		U16 occupiedSq = playerLoc.x / _pGameCamera->TILE_DIM;

		U16 tileX;

		// Right Shift Amount +
		if(shiftAmount.x > 0)
		{
			// Draw RightSide Screen

			cursorLoc.x = _pGameCamera->getBottomRight().x - shiftAmount.x;
			cursorLoc.y = _pGameCamera->getTopLeft().y;

			if(_pGameCamera->getTopLeft().x % _pGameCamera->TILE_DIM == 0)
				occupiedSq -=1;

			tileX = (_pGameCamera->getBottomRight().x - 1) / _pGameCamera->TILE_DIM;			// get tile x in Tile Based

			// Draw Left Side of Image Player was On

		}
		// Left Shift Amount -
		else
		{
			influence += 1;

			cursorLoc.x = _pGameCamera->getTopLeft().x;
			cursorLoc.y = _pGameCamera->getTopLeft().y;
			tileX = (_pGameCamera->getTopLeft().x) / _pGameCamera->TILE_DIM;			// get tile x in Tile Based
		}

		// Draw Layer Being Seen
		for (U16 k = cursorLoc.y / _pGameCamera->TILE_DIM; k < _pGameCamera->getBottomRight().y / _pGameCamera->TILE_DIM; k++)
		{
			_pGameCamera->setCursorPosition(_pGameCamera->getTopLeft().x, k * _pGameCamera->TILE_DIM);
			// Draw Tile
			_pWorldMapTile[k][tileX]->Image().DrawVerticalLine(cursorLoc,_pGameCamera);

			cursorLoc.y += _pGameCamera->TILE_DIM;
		}

		// Draws Tile Under Character
		cursorLoc = playerLoc;

		cursorLoc.x += influence * _pGameCamera->TILE_DIM - 1 + influence;
		// Draw Tile Being Exposed from Character
		_pWorldMapTile[playerLoc.y / _pGameCamera->TILE_DIM][occupiedSq + influence]->Image().DrawVerticalLine(cursorLoc,_pGameCamera);
	}

	// Shift Y
	else if(shiftAmount.y)
	{
		U16 occupiedSq = playerLoc.y / _pGameCamera->TILE_DIM;

		U16 tileY;

		// Down	Shift Amount +
		if(shiftAmount.y > 0)
		{

			// Draw Bottom Screen

			cursorLoc.x = _pGameCamera->getTopLeft().x;
			cursorLoc.y = _pGameCamera->getBottomRight().y - shiftAmount.y;

			tileY = (_pGameCamera->getBottomRight().y - 1) / _pGameCamera->TILE_DIM;			// get tile x in Tile Based


			if(_pGameCamera->getTopLeft().y % _pGameCamera->TILE_DIM == 0)
				occupiedSq -=1;

			// Draw Left Side of Image Player was On

		}
		// Up	Shift Amount -
		else
		{
			influence += 1;

			cursorLoc.x = _pGameCamera->getTopLeft().x;
			cursorLoc.y = _pGameCamera->getTopLeft().y;

			tileY = (_pGameCamera->getTopLeft().y) / _pGameCamera->TILE_DIM;			// get tile x in Tile Based
		}

		// Draw Layer Being Seen
		for (U16 k = cursorLoc.x / _pGameCamera->TILE_DIM; k < _pGameCamera->getBottomRight().x / _pGameCamera->TILE_DIM; k++)
		{
			// Draw Tile
			_pWorldMapTile[tileY][k]->Image().DrawHorizontalLine(cursorLoc,_pGameCamera);

			cursorLoc.x += _pGameCamera->TILE_DIM;
		}


		// Draws Tile Under Character
		cursorLoc = playerLoc;

		cursorLoc.y += influence * _pGameCamera->TILE_DIM - 1 + influence;
		// Draw Tile Being Exposed from Character
		_pWorldMapTile[occupiedSq + influence][playerLoc.x / _pGameCamera->TILE_DIM]->Image().DrawHorizontalLine(cursorLoc,_pGameCamera);


	}

}


Tile * World::GetTile(U16 x, U16 y)
{
	return _pWorldMapTile[y][x];
}


